package com.wjerp.tplus.server.common.config;

import com.wjerp.tplus.server.common.security.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDecisionVoter;
import org.springframework.security.access.vote.AffirmativeBased;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.access.expression.WebExpressionVoter;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import javax.annotation.Resource;
import java.util.Arrays;
import java.util.List;


/**
 * @author lishuailei
 */
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter{
	
	@Autowired
	private JwtAuthenticationEntryPoint unauthorizedHandler;

	@Resource
	private JwtPlatformUserInfoServiceImpl jwtPlatformUserInfoService;

	@Autowired
	public void configureAuthentication(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
		authenticationManagerBuilder
		.userDetailsService(this.jwtPlatformUserInfoService)
		.passwordEncoder(passwordEncoder());
	}

	@Bean
	@Override
	public AuthenticationManager authenticationManagerBean() throws Exception {
		return super.authenticationManagerBean();
	}

	@Bean
	public PasswordEncoder passwordEncoder() {
		return new BCryptPasswordEncoder();
	}

	@Bean
	public JwtAuthenticationTokenFilter authenticationTokenFilterBean() throws Exception {
		return new JwtAuthenticationTokenFilter();
	}

	@Bean
	public CustomSecurityMetadataSource customSecurityMetadataSource(){
		return new CustomSecurityMetadataSource();
	}

	@Bean
	public AccessDecisionManager accessDecisionManager(){
		List<AccessDecisionVoter<? extends Object>> decisionVoters
	      = Arrays.asList(
	        new WebExpressionVoter(),
	        new CustomRoleVoter());
	        //new AuthenticatedVoter());
	    return new AffirmativeBased(decisionVoters);
	}
	
	@Bean
    CorsConfigurationSource corsConfigurationSource() {
		CorsConfiguration configuration = new CorsConfiguration();
		configuration.setAllowedOrigins(Arrays.asList("*"));
		configuration.setAllowedMethods(Arrays.asList("GET","POST","PUT","DELETE","OPTIONS"));
		configuration.setAllowedHeaders(Arrays.asList("Authorization", "Cache-Control", "Content-Type","X-Requested-With"));
		UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
		source.registerCorsConfiguration("/**", configuration);
		return source;
	}

	@Override
	public void configure(WebSecurity web) throws Exception {
		web.ignoring().antMatchers(
				"/v2/api-docs",
				"/swagger-resources/configuration/ui",
				"/swagger-resources",
				"/swagger-resources/configuration/security",
				"/swagger-ui.html",
				"/",
				"/test",
				"/*.html",
				"/favicon.ico",
				"/**/*.html",
				"/**/*.css",
				"/**/*.js",
				"/**/*.ttf",
				"/**/*.png",
				"/image/**");
	}

	@Override
	protected void configure(HttpSecurity httpSecurity) throws Exception {
		httpSecurity.cors().and()
		// we don't need CSRF because our token is invulnerable
		.csrf().disable()
		.exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
		// don't create session
		.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
		.authorizeRequests()
		.antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
		// allow anonymous resource requests
		.antMatchers(
				HttpMethod.GET,
				"/",
				"/test",
				"/*.html",
				"/favicon.ico",
				"/**/*.html",
				"/**/*.css",
				"/**/*.js",
				"/**/*.ttf",
				"/**/*.png",
				"/image/**"
				).permitAll()
				.antMatchers("/auth/**").permitAll()
				.antMatchers("/v2/api-docs").permitAll()
				.antMatchers("/v2/api-docs/**").permitAll()
				.antMatchers("/swagger-resources/configuration/ui").permitAll()
				.antMatchers("/swagger-ui.html").permitAll()
				.antMatchers("/swagger-resources").permitAll()
				.antMatchers("/swagger-resources/configuration/security").permitAll()
				.antMatchers("/swagger-resources/**").permitAll()
				.antMatchers("/webjars/**").permitAll()
				.antMatchers("/test/**").permitAll()
				.antMatchers("/user/sendForgetPasswordSMSCode").permitAll()
				.antMatchers("/user/captcha").permitAll()
				.antMatchers("/user/questions").permitAll()
				.antMatchers("/user/verifyForgetPasswordSMSCode").permitAll()
				.antMatchers("/user/sendRegisterSMSCode").permitAll()
				.antMatchers("/user/register").permitAll()
				.antMatchers("/user/password").permitAll()
				.antMatchers("/user/verify_email_bind").permitAll()
				.antMatchers("/notification/**").permitAll()
				//由于现在处于接口mock阶段，现在暂时对所有接口都不做认证
				//.antMatchers("/**").permitAll()
				.anyRequest().authenticated()
				.withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
					@Override
					public <O extends FilterSecurityInterceptor> O postProcess(
							O fsi) {
						
						fsi.setSecurityMetadataSource(customSecurityMetadataSource());
						fsi.setAccessDecisionManager(accessDecisionManager());
						fsi.setRejectPublicInvocations(false);
						return fsi;
					}
				});
		
		// Custom JWT based security filter
		httpSecurity
		.addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class);
		
		// disable page caching
		httpSecurity.headers().cacheControl();  
	}
}
